/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/
//用常数作为状态，用的时候不容易写错
const PENDING="pending"   //等待
const FULFILLED="fulfilled"  //成功
const REJECTED="rejected" //失败

class MyPromise{
    
    //构造函数传递一个执行器
    constructor(executor){
        try{
            //执行器立即执行
            executor(this.resolve,this.reject)
        }catch(e){
            this.reject(e)
        }
        
    }

    //状态
    status=PENDING

    value
    reason

    successCallback=[]
    failCallback=[]

    resolve=value=>{
        //阻止状态多次更改
        if(this.status!=PENDING) return
        this.status=FULFILLED
        //保存成功的值
        this.value=value

        while(this.successCallback.length) this.successCallback.shift()()
    }
    reject=reason=>{
        //阻止状态多次更改
        if(this.status!=PENDING) return
        this.status=REJECTED
        //保存失败的原因
        this.reason=reason

        while(this.failCallback.length) this.failCallback.shift()()
    }

    then(successCallback,failCallback){
        //如果没有传入参数，或者参数不是函数 直接吧接收到的值传给下一个then
        if(!(successCallback instanceof Function)){
            successCallback=v=>v
        }
        let promise2=new MyPromise((resolve,reject)=>{
            if(this.status==FULFILLED){
                //then里面的回调是放入微任务队列的
                queueMicrotask(()=>{
                    try{
                        let x=successCallback(this.value)
                        resolvePromise(promise2,x,resolve,reject)
                    }catch(e){
                        reject(e)
                    }
                })
            }else if(this.status==REJECTED){
                queueMicrotask(()=>{
                    if(failCallback instanceof Function){
                        try {
                            let x=failCallback(this.reason)
                            resolvePromise(promise2,x,resolve,reject)
                        } catch (e) {
                            reject(e)
                        }
                    }else{
                        //没有错误回调函数时,把当前错误传递给下一个失败函数
                        reject(this.reason)
                    }
                })
            }else{
                this.successCallback.push(()=>{
                    try{
                        let x=successCallback(this.value)
                        resolvePromise(promise2,x,resolve,reject)
                    }catch(e){
                        reject(e)
                    } 
                })
                this.failCallback.push(()=>{
                    if(failCallback instanceof Function){
                        try{
                            let x=failCallback(this.reason)
                            resolvePromise(promise2,x,resolve,reject)
                        }catch(e){
                            reject(e)
                        }
                    }else{
                        //没有错误回调函数时,把当前错误传递给下一个失败函数
                        reject(this.reason)
                    }
                })
            }
        })
        return promise2
    }

    finally(callback){
        return this.then(value=>{
            return MyPromise.resolve(callback()).then(()=>value)
            
        },reason=>{
            return MyPromise.resolve(callback()).then(()=>{throw reason})
        })
    }

    catch(failCallback){
        return this.then(undefined,failCallback)
    }

    static all(array){
        //结果数组
        let result=[]
        let index=0 //计数
        
        return new MyPromise((resolve,reject)=>{
            function addData(key,value){
                result[key]=value
                index++
                if(index===array.length){
                    resolve(result)
                }
            }
            for(let i=0;i<array.length;i++){
                let current=array[i]
                if(current instanceof MyPromise){
                    current.then(value=>addData(i,value),reason=>reject(reason))
                }else{
                    //普通值直接放到结果数组
                    addData(i,array[i])
                }
            }
        })
    }

    static resolve(value){
        if (value instanceof MyPromise) return value;
        return new MyPromise(resolve=>resolve(value))
    }

    static race(array){
        return new MyPromise((resolve,reject)=>{
            for(let i=0;i<array.length;i++){
                let v=array[i]
                if(v instanceof MyPromise){
                    v.then(resolve,reject)  
                }else{
                    resolve(v) //普通值 直接resolve
                }
            }
        })
       
    }
}

function resolvePromise(promise2,x,resolve,reject){
    //防止自己返回自己的情况
    if(promise2===x){
        return reject(new TypeError("Chaining cycle detected for promise #<Promise>"))
    }

    if(x instanceof MyPromise){
        x.then(resolve,reject)
    }else{
    
        resolve(x)
       
    }

}

module.exports=MyPromise



